#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <fstream>
#include <mutex>
#include "util.hpp"


namespace Index
{
    struct DocInfo
    {
        std::string title;      //文档的标题
        std::string content;    //文档的内容
        std::string url;        //文档的url
        uint64_t id;            //文档的ID
    };

    struct InvertedElem
    {
        uint64_t id;           //文档的ID
        std::string word;      //文档中的关键字
        int weight;            //权重
    };

    typedef std::vector<InvertedElem> InvertedList; //倒排拉链
    class index
    {
    private:
        std::vector<DocInfo> forward_index; //正排索引
        std::unordered_map<std::string, InvertedList> inverted_index; //倒排索引
    private:
        static index* instance;
        static std::mutex mtx;
        index(){}
        index(const index&) = delete;
        index& operator=(const index&) = delete;
    public:
        ~index(){}

        static index* GetInstance()
        {
            if(nullptr == instance)
            {
                mtx.lock();
                if(nullptr == instance)
                {
                    instance = new index();
                }
                mtx.unlock();
            }

            return instance;
        }

        //根据文档id，获取文档内容
        DocInfo* GetDocInfo(const uint16_t& id)
        {
            if(id >= forward_index.size())
            {
                LOG(DEBUG, "id out of range");
                return nullptr;
            }
            return &forward_index[id];
        }

        //根据关键字，获取倒排拉链
        InvertedList* GetInvertedList(const std::string& word)
        {
            auto iter = inverted_index.find(word);
            if(iter == inverted_index.end())
            {
                LOG(WARNING, " not exitst");
                return nullptr;
            }
            return &inverted_index[word];
        }

        //构建索引
        bool BuildIndex(const std::string& input) //数据所在的路径
        {
            std::ifstream ifs(input, std::ios::in | std::ios::binary);
            if(!ifs.is_open())
            {
                LOG(DEBUG, "open " + input + " failed");
                return false;
            }
            std::string line;
            int cnt = 0;
            while(std::getline(ifs, line))
            {
                //构建正牌索引
                DocInfo* doc = BuildForwardIndex(line);
                if(nullptr == doc)
                {
                    LOG(WARNING, "构建失败" + line);
                    continue;
                }
                //构建倒排索引
                BuildInvertedList(*doc);
                ++cnt;
                if(cnt % 100 == 0)
                {
                    LOG(NORMAL, "已经构建了 " + std::to_string(cnt) + "多少个索引");
                }
            }
            
            return true;
        }
    private:
        DocInfo* BuildForwardIndex(const std::string& input)
        {
            //1.切分字符串
            const std::string sep = "\3";
            std::vector<std::string> results;
            Util::StringUtil::Split(input, &results, sep);
            //2.填充字符串
            if(results.size() != 3)
            {
                LOG(WARNING, "error");
                return nullptr;
            }
            DocInfo doc;
            doc.title = results[0];
            doc.content = results[1];
            doc.url = results[2];
            doc.id = forward_index.size();
            //3.插入
            forward_index.push_back(std::move(doc));

            return &forward_index.back();
        }

        bool BuildInvertedList(const DocInfo& doc)
        {
            struct word_cnt
            {   
                int title_cnt;
                int content_cnt;
                word_cnt(): title_cnt(0), content_cnt(0){}
            };
            std::unordered_map<std::string, word_cnt> word_map;
            //对标题进行词频统计
            std::vector<std::string> title_words;
            Util::CutUtil::CutString(doc.title, &title_words);
            for(auto s : title_words)
            {
                boost::to_lower(s);
                word_map[s].title_cnt++;
            }
            //对内容进行词频统计
            std::vector<std::string> content_words;
            Util::CutUtil::CutString(doc.content, &content_words);
            for(auto s : content_words)
            {
                boost::to_lower(s);
                word_map[s].content_cnt++;
            }
#define X 10
#define Y 1
            //构建倒排索引
            for(auto& word : word_map)
            {
                InvertedElem item;
                item.id = doc.id;
                item.word = word.first;
                item.weight = X * (word.second.title_cnt) + Y * (word.second.content_cnt);
                InvertedList& invertedlist = inverted_index[word.first];
                invertedlist.push_back(std::move(item)); 
            }

            return true;
        }
    };
    index* index::instance = nullptr;
    std::mutex index::mtx;
}